package data_deepprocessing.algorithm.crfs;

import java.io.File;
import java.util.ArrayList;
import java.util.Collections;
import java.util.HashMap;
import java.util.List;
import java.util.Map;
import java.util.TreeMap;
import java.util.logging.Logger;


import data_deepprocessing.algorithm.crfs.beans.SeedInfo2CRFsBean;
import data_deepprocessing.prepareData.beans.XianBingShi_New_Bean;
import data_deepprocessing.prepareData.beans.XianBingShi_new_zhangBean;
import data_deepprocessing.prepareData.beans.YYH_XianBingShiBean;
import data_deepprocessing.prepareData.beans.ZhuSu_New_Bean;
import data_deepprocessing.util.TxtOperate;




/**
 * @author Yuanyuhu
 *  这里面的改进的一点就是用了最新的KMP算法
 *  这里先不使用springMVC的方法，等回头再改这方面面的内容
 */
public class CreateCRFsDataSet {
	
	static Logger logger = Logger.getLogger(CreateCRFsDataSet.class.getName());
	
	public static List<Integer> next = new ArrayList<Integer>();
	/**
	 * 这里的integrate指的是bootstrapping and 结构化
	 * 这里可以用taged_Entity_Info也可以用bootstrapping生成的sym_seed_rule   同时也可以 用Main规范boostrapping后的
	 */
//	static String path = "D:\\Experiment\\new\\yuanExperimentDataSet20161116\\YuanYuhuExperimentDataSet_all\\";
//	
//	public static final String trainPath = path+"train";
//	
//	public static final String standardDataSetPath = path+"StandardDataSet";
//	
//	public static final String testPath = path+"test";
	
	public static void main(String[] args) throws Exception{
		//生成训练集
//		List<XianBingShi_Content_Bean> xiangbingshiInfoList = CRF_YUAN_DB_New.getTrainSet();
//		CRFTagServiceUpgrade_Zhang.doCreateCrfDataUpGradeThree(trainPath,xiangbingshiInfoList,"S");
		
		//生成测试集
//		List<XianBingShi_Content_Bean> xianbingshiInfo = CRF_YUAN_DB_New.getTestSet();
//		CRFTagServiceUpgrade_Yuan.doCreateCRFTest(testPath, xianbingshiInfo);
		
		
		//生成标准集
//		List<YYH_XianBingShiBean> xiangbingshiInfoList = CRF_YUAN_DB_New.getStandardDataSetPathSet();
//		CreateCRFsDataSet.doCreateCrfDataUpGradeThree(standardDataSetPath,xiangbingshiInfoList,"S");
	}
	
	
	/**
	 * 生成种子内容的相关信息，并对种子利用贪心算法进行筛选
	 * @param chunkInfo   
	 * @param initSeedInfoMap
	 * @return
	 * Map<起始位置, InitSeedInfo>
	 * 
	 */
	
	private  Map<Integer, SeedInfo2CRFsBean> getFilteredInitSeedInfo(Map<Integer, String> initSeedInfoMap){
		Map<Integer, SeedInfo2CRFsBean> initSeedInfoMapUp = new TreeMap<Integer, SeedInfo2CRFsBean>();
		List<SeedInfo2CRFsBean> initSeedInfos = new ArrayList<SeedInfo2CRFsBean>();
		List<SeedInfo2CRFsBean> tempList = new ArrayList<SeedInfo2CRFsBean>();
		for(Integer locateInfo : initSeedInfoMap.keySet()){
			SeedInfo2CRFsBean initSeedInfo = new SeedInfo2CRFsBean();
			int length = initSeedInfoMap.get(locateInfo).length();
			initSeedInfo.setStartPro(locateInfo);
			initSeedInfo.setLength(length);
			initSeedInfo.setContent(initSeedInfoMap.get(locateInfo));
			initSeedInfo.setSumLength(locateInfo+length);
			initSeedInfos.add(initSeedInfo);
		}
		
		for(SeedInfo2CRFsBean initSeedInfo :initSeedInfos){
			logger.info(initSeedInfo+":unsorted");
		}
		
		initSeedInfoMap.clear();
		Collections.sort(initSeedInfos);
		
		for(SeedInfo2CRFsBean initSeedInfo :initSeedInfos){
			logger.info(initSeedInfo+":sorted");
		}
		
		
		tempList.add(initSeedInfos.get(0));
		SeedInfo2CRFsBean tempSeed = tempList.get(0);
		for(int i=1; i< initSeedInfos.size(); ++i){
			if(initSeedInfos.get(i).getStartPro()>=tempSeed.getSumLength()){
				tempList.add(initSeedInfos.get(i));
				tempSeed = initSeedInfos.get(i);
			}
		}
		
		for(SeedInfo2CRFsBean info: tempList){
			System.out.println(info +"：最初的筛选");
			initSeedInfoMapUp.put(info.getStartPro(), info);
		}
		
		return initSeedInfoMapUp;
	}
	
	
	
	/**
	 * 直接给出种子词init_seed，就能利用表yyh_tag_chunk_info生成相应的CRFs的训练集数据
	 * 1：给出种子词
	 * 2：种子词的起始位置+种子词的长度
	 * 3：要标注的sentence
	 * 要解决的问题是：一句话中有重复的词时，只能标出最前面的一个，这里要标注出所有的症状的词
	 * 缺陷是不能处理全部是相同字的种子：例如“吭吭”；要解决这个问题估计得重写nextval和kmp
	 * 还有就是对种子词按越小越好来处理
	 * 这部分的内容的思想是：对所有的种子词只标注一次，其他的就不管了，这样也就不容易产生不可预料的冲突
	 * 想要避免种子词的重复，最好的还是对句子进行切分：这样会很有利与对效果的提高
	 * 
	 * 还有就是现在程序中：在标注在同一个位置时选择的都是短的种子词，这部分会对之后的召回率和准确率构成影响
	 * 后续的实验可以在这个上面做一下
	 * 
	 */
	public void doCreateCrfDataUpGradeThree(String filePath,List<String> initSeeds ,List<XianBingShi_New_Bean> xianbingshiInfos,String label) throws Exception{
		for(XianBingShi_New_Bean xianBingShiBean : xianbingshiInfos){
			//Map<种子的起始位置，种子内容>
			Map<Integer, String> seedInfoMap =  new HashMap<Integer, String>();
			Map<Integer, SeedInfo2CRFsBean> initSeedInfoMap = new HashMap<Integer, SeedInfo2CRFsBean>();
			int chunkId = xianBingShiBean.getYuan_id();
			String chunkContent = xianBingShiBean.getSubxianbingshi();
			String newchunkContent="";
			/**
			 * 这一块把所有能匹配上某一chunkContent的种子词，放到map里面
			 * 若定位到了同一位置上则，选择字符串较短的那一个
			 */
			int startLocat = 0;
			for(String seed : initSeeds){
				System.out.println(seed+"start");
				GetNextval(seed);
				startLocat = KmpSearch(chunkContent, seed);
				if(startLocat != -1){//change
					if(!seedInfoMap.containsKey(startLocat)){
						seedInfoMap.put(startLocat, seed);
					}else{
						logger.info(seed+"2");
						//如果两个种子词出现在同一个地方，如果小的那个存进去，这里就保证了，标注更多的种子词，其实这里应该保证标注粒度更细的种子词
						//这里是一处关键地点，回头如果程序结果不理想，可以改回来
						if(seed.length() > seedInfoMap.get(startLocat).length()){
							seedInfoMap.put(startLocat, seed);
						}
					}
				}
				//这里的next里面是KMP要用到的中间数据
				if(next.size()!= 0){
					next.clear();
				}
			}
			//测试的时候能这样打印出来，真正跑的时候还是把这部分的内容给注释了
			for(Integer integer:seedInfoMap.keySet()){
				System.out.println("Map's content :"+integer+"="+seedInfoMap.get(integer));
			}
			
			/**
			 * 用贪心算法解决如果有种子存在包含或者交叉的关系
			 * 就跟举行活动的问题一样
			 */
			if(seedInfoMap.size()!=0){
				initSeedInfoMap = getFilteredInitSeedInfo(seedInfoMap);
			}
			
			/**
			 * 用最终得到的种子词对chunkContent进行标注
			 * 可以用这样的结果作为训练集
			 */
			for(int i=0;i<=chunkContent.length()-1;i++){
				String str1=chunkContent.substring(i,i+1); 
				if(initSeedInfoMap.size()!=0){
					for(Integer startpro : initSeedInfoMap.keySet()){
						int seedLength = initSeedInfoMap.get(startpro).getLength();
						int k=0;
						if(i==startpro){
							while(k<seedLength){
								if(i<=chunkContent.length()-1){
									str1=chunkContent.substring(i,i+1);
									if(k==0){
										newchunkContent=newchunkContent+str1+"\tB-"+label+"\r\n";
									}else{
										newchunkContent=newchunkContent+str1+"\tE-"+label+"\r\n";
									}
									k++;
									i++;
								}
							}
						}
					}
					if(i<=chunkContent.length()-1){
						str1=chunkContent.substring(i,i+1); 
						newchunkContent=newchunkContent+str1+"\tO"+"\r\n";	
					}
				}else{
					newchunkContent=newchunkContent+str1+"\tO"+"\r\n";	
				}
			}
			String fileName=String.valueOf(chunkId);
			File f=TxtOperate.newTxt(filePath, fileName);
			TxtOperate.writeTxtFile(newchunkContent, f, true);
		}
	}
	
	public void doCreateCrfDataUpGradeThree2ZhuSu(String filePath,List<String> initSeeds ,List<ZhuSu_New_Bean> zhusuInfos,String label) throws Exception{
		for(ZhuSu_New_Bean zhuSuBean : zhusuInfos){
			//Map<种子的起始位置，种子内容>
			Map<Integer, String> seedInfoMap =  new HashMap<Integer, String>();
			Map<Integer, SeedInfo2CRFsBean> initSeedInfoMap = new HashMap<Integer, SeedInfo2CRFsBean>();
			int chunkId = zhuSuBean.getChunk_id();
			String chunkContent = zhuSuBean.getHandled_content();
			String newchunkContent="";
			/**
			 * 这一块把所有能匹配上某一chunkContent的种子词，放到map里面
			 * 若定位到了同一位置上则，选择字符串较短的那一个
			 */
			int startLocat = 0;
			for(String seed : initSeeds){
				System.out.println(seed+"start");
				GetNextval(seed);
				startLocat = KmpSearch(chunkContent, seed);
				if(startLocat != -1){//change
					if(!seedInfoMap.containsKey(startLocat)){
						seedInfoMap.put(startLocat, seed);
					}else{
						logger.info(seed+"2");
						//如果两个种子词出现在同一个地方，如果小的那个存进去，这里就保证了，标注更多的种子词，其实这里应该保证标注粒度更细的种子词
						//这里是一处关键地点，回头如果程序结果不理想，可以改回来
						if(seed.length() > seedInfoMap.get(startLocat).length()){
							seedInfoMap.put(startLocat, seed);
						}
					}
				}
				//这里的next里面是KMP要用到的中间数据
				if(next.size()!= 0){
					next.clear();
				}
			}
			//测试的时候能这样打印出来，真正跑的时候还是把这部分的内容给注释了
			for(Integer integer:seedInfoMap.keySet()){
				System.out.println("Map's content :"+integer+"="+seedInfoMap.get(integer));
			}
			
			/**
			 * 用贪心算法解决如果有种子存在包含或者交叉的关系
			 * 就跟举行活动的问题一样
			 */
			if(seedInfoMap.size()!=0){
				initSeedInfoMap = getFilteredInitSeedInfo(seedInfoMap);
			}
			
			/**
			 * 用最终得到的种子词对chunkContent进行标注
			 * 可以用这样的结果作为训练集
			 */
			for(int i=0;i<=chunkContent.length()-1;i++){
				String str1=chunkContent.substring(i,i+1); 
				if(initSeedInfoMap.size()!=0){
					for(Integer startpro : initSeedInfoMap.keySet()){
						int seedLength = initSeedInfoMap.get(startpro).getLength();
						int k=0;
						if(i==startpro){
							while(k<seedLength){
								if(i<=chunkContent.length()-1){
									str1=chunkContent.substring(i,i+1);
									if(k==0){
										newchunkContent=newchunkContent+str1+"\tB-"+label+"\r\n";
									}else{
										newchunkContent=newchunkContent+str1+"\tE-"+label+"\r\n";
									}
									k++;
									i++;
								}
							}
						}
					}
					if(i<=chunkContent.length()-1){
						str1=chunkContent.substring(i,i+1); 
						newchunkContent=newchunkContent+str1+"\tO"+"\r\n";	
					}
				}else{
					newchunkContent=newchunkContent+str1+"\tO"+"\r\n";	
				}
			}
			String fileName=String.valueOf(chunkId);
			File f=TxtOperate.newTxt(filePath, fileName);
			TxtOperate.writeTxtFile(newchunkContent, f, true);
		}
	}
	public void doCreateCrfDataUpGradeThree2CRFNew(String filePath,List<String> initSeeds ,List<XianBingShi_new_zhangBean> xianbingshiInfos,String label) throws Exception{
		for(XianBingShi_new_zhangBean xianBingShiBean : xianbingshiInfos){
			//Map<种子的起始位置，种子内容>
			Map<Integer, String> seedInfoMap =  new HashMap<Integer, String>();
			Map<Integer, SeedInfo2CRFsBean> initSeedInfoMap = new HashMap<Integer, SeedInfo2CRFsBean>();
			int chunkId = xianBingShiBean.getId();
			String chunkContent = xianBingShiBean.getSubxianbingshi();
			String newchunkContent="";
			/**
			 * 这一块把所有能匹配上某一chunkContent的种子词，放到map里面
			 * 若定位到了同一位置上则，选择字符串较短的那一个
			 */
			int startLocat = 0;
			for(String seed : initSeeds){
				System.out.println(seed+"start");
				GetNextval(seed);
				startLocat = KmpSearch(chunkContent, seed);
				if(startLocat != -1){//change
					if(!seedInfoMap.containsKey(startLocat)){
						seedInfoMap.put(startLocat, seed);
					}else{
						logger.info(seed+"2");
						//如果两个种子词出现在同一个地方，如果小的那个存进去，这里就保证了，标注更多的种子词，其实这里应该保证标注粒度更细的种子词
						//这里是一处关键地点，回头如果程序结果不理想，可以改回来
						if(seed.length() > seedInfoMap.get(startLocat).length()){
							seedInfoMap.put(startLocat, seed);
						}
					}
				}
				//这里的next里面是KMP要用到的中间数据
				if(next.size()!= 0){
					next.clear();
				}
			}
			//测试的时候能这样打印出来，真正跑的时候还是把这部分的内容给注释了
			for(Integer integer:seedInfoMap.keySet()){
				System.out.println("Map's content :"+integer+"="+seedInfoMap.get(integer));
			}
			
			/**
			 * 用贪心算法解决如果有种子存在包含或者交叉的关系
			 * 就跟举行活动的问题一样
			 */
			if(seedInfoMap.size()!=0){
				initSeedInfoMap = getFilteredInitSeedInfo(seedInfoMap);
			}
			
			/**
			 * 用最终得到的种子词对chunkContent进行标注
			 * 可以用这样的结果作为训练集
			 */
			for(int i=0;i<=chunkContent.length()-1;i++){
				String str1=chunkContent.substring(i,i+1); 
				if(initSeedInfoMap.size()!=0){
					for(Integer startpro : initSeedInfoMap.keySet()){
						int seedLength = initSeedInfoMap.get(startpro).getLength();
						int k=0;
						if(i==startpro){
							while(k<seedLength){
								if(i<=chunkContent.length()-1){
									str1=chunkContent.substring(i,i+1);
									if(k==0){
										newchunkContent=newchunkContent+str1+"\tB-"+label+"\r\n";
									}else{
										newchunkContent=newchunkContent+str1+"\tE-"+label+"\r\n";
									}
									k++;
									i++;
								}
							}
						}
					}
					if(i<=chunkContent.length()-1){
						str1=chunkContent.substring(i,i+1); 
						newchunkContent=newchunkContent+str1+"\tO"+"\r\n";	
					}
				}else{
					newchunkContent=newchunkContent+str1+"\tO"+"\r\n";	
				}
			}
			String fileName=String.valueOf(chunkId);
			File f=TxtOperate.newTxt(filePath, fileName);
			TxtOperate.writeTxtFile(newchunkContent, f, true);
		}
	}
	

	
	private int KmpSearch(String text, String pattern) {
		if(text == null || pattern==null){
			return -1;
		}
		
		if ((pattern.length() == 0) || (text.length() == 0))
			return -1;

		int i = 0, j = 0;
		int lens = text.length();
		int lenp = pattern.length();

		while (i < lens && j < lenp) {
			if (text.charAt(i) == pattern.charAt(j)) {
				i++;
				j++;
			} else if (j == 0)
				i++;
			else
				j = next.get(j - 1) + 1;
		}
		return ((j == lenp) ? (i - lenp) : -1);

	}

	private void GetNextval(String pattern) {

		int M = pattern.length();

		if (M > 0) {
			int[] table = new int[M];
			table[0] = -1;

			for (int j = 1; j < M; j++) {
				int i = table[j - 1]; // index on pattern string
				while ((pattern.charAt(j) != pattern.charAt(i + 1)) && i >= 0)
					i = table[i];
				if (pattern.charAt(j) == pattern.charAt(i + 1))
					table[j] = i + 1;
				else
					table[j] = -1;
			}

			for(int i: table){
				next.add(i);
			}
		} else {
			next.add(-1);
		}
	}
	
	/**
	 * 将未标注的数据生成为CRF测试集，
	 * 这里只是把list中的内容给形成测试集的数据
	 * @param filePath
	 * @param list
	 * @throws Exception
	 */
	public void doCreateCRFTest(String filePath,List<XianBingShi_New_Bean> list) throws Exception{		
		logger.info((list.size()+"没有CRF标注的病历块"));
		for(XianBingShi_New_Bean xianbingshiInfo : list){
			int chunkId= xianbingshiInfo.getYuan_id();
			String xianbingshi= xianbingshiInfo.getSubxianbingshi();
			String newChunkContent = "";
			for(int j=0; j< xianbingshi.length();j++){
				String str1 = xianbingshi.substring(j,j+1);  
				newChunkContent=newChunkContent+str1+"\tO"+"\r\n";
			}
			String fileName=String.valueOf(chunkId);
			File f=TxtOperate.newTxt(filePath, fileName);
			TxtOperate.writeTxtFile(newChunkContent, f, true);
		}
	}

}





